package lxs.swift.tool;


import lxs.swift.collector.Page;
import lxs.swift.verify.Verifies;

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;


public class Lister {

    /**
     * 将一个列表中每个元素的其中两个字段组合为一个map
     */
    public static <T, K, V> Map<K, V> cut(Collection<T> collection, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        Map<K, V> map = new HashMap<>();
        if (Verifies.isEmpty(collection)) return map;
        Objects.requireNonNull(keyMapper);
        Objects.requireNonNull(valueMapper);
        collection.forEach(item -> {
            K k = keyMapper.apply(item);
            V v = valueMapper.apply(item);
            map.put(k, v);
        });
        return map;
    }

    /**
     * 将一个列表按key映射为map
     */
    public static <T, K> Map<K, T> cut(Collection<T> collection, Function<T, K> keyMapper) {
        return cut(collection, keyMapper, s -> s);
    }

    public static <T, R> List<R> map(Collection<T> collection, Function<T, R> mapper) {
        List<R> list = new ArrayList<>();
        collection.forEach(item -> list.add(mapper.apply(item)));
        return list;
    }

    public static <T> List<T> filter(Collection<T> collection, Predicate<T> predicate) {
        List<T> list = new ArrayList<>();
        collection.forEach(item -> {
            if (predicate.test(item)) {
                list.add(item);
            }
        });
        return list;
    }

    /**
     * 将一个列表按key映射为map
     */
    public static <T, K> Map<K, List<T>> group(Collection<T> collection, Function<T, K> keyMapper) {
        Objects.requireNonNull(collection);
        Objects.requireNonNull(keyMapper);
        Map<K, List<T>> map = new HashMap<>();
        for (T t : collection) {
            K k = keyMapper.apply(t);
            map.computeIfAbsent(k, key -> new ArrayList<>()).add(t);
        }
        return map;
    }

    /**
     * 将一个列表按key映射为map
     */
    public static <T, K, V> Map<K, V> groupReduce(Collection<T> collection,
                                                  Function<T, K> keyMapper,
                                                  Function<T, V> valueMapper,
                                                  BiFunction<V, V, V> reducer,
                                                  V defaultValue) {
        Objects.requireNonNull(collection);
        Objects.requireNonNull(keyMapper);
        Objects.requireNonNull(valueMapper);
        Map<K, V> map = new HashMap<>();
        for (T t : collection) {
            K k = keyMapper.apply(t);
            V origin = map.computeIfAbsent(k, key -> defaultValue);
            V v = valueMapper.apply(t);
            V nv = reducer.apply(v, origin);
            map.put(k, nv);
        }
        return map;
    }

    /**
     * 将两个列表转为一个map
     */
    public static <K, V> Map<K, V> zip(Collection<K> keyCollection, Collection<V> valueCollection) {
        Map<K, V> map = new HashMap<>();
        if (Verifies.isEmpty(keyCollection) || Verifies.isEmpty(valueCollection)) return map;
        Iterator<V> iterator = valueCollection.iterator();

        Lister.enumerate(keyCollection, (k, i) -> {
            V v = iterator.hasNext() ? iterator.next() : null;
            map.put(k, v);
        });
        return map;
    }

    /**
     * 提取不在base集合中的值
     */
    public static <T, R> List<R> notIn(Collection<T> base, Function<T, R> mapper, Collection<T> valueCollection) {
        Set<R> baseSet = base.stream().map(mapper).collect(Collectors.toSet());
        List<R> list = new ArrayList<>();
        for (T value : valueCollection) {
            R r = mapper.apply(value);
            if (!baseSet.contains(r)) {
                list.add(r);
            }
        }
        return list;
    }

    /**
     * 将map转换为一组元素
     */
    public static <T, K, V> List<T> knit(Map<K, V> map, BiFunction<K, V, T> mapping) {
        List<T> list = new ArrayList<>();
        if (Verifies.isEmpty(map)) return list;
        Objects.requireNonNull(mapping);
        map.forEach((k, v) -> list.add(mapping.apply(k, v)));
        return list;
    }

    public static <T> void backEach(Collection<T> collection, BiConsumer<T, Integer> consumer) {
        if (Verifies.isEmpty(collection)) return;
        ArrayList<T> arrayList = new ArrayList<>(collection);
        for (int i = arrayList.size() - 1; i >= 0; i--) {
            consumer.accept(arrayList.get(i), i);
        }
    }

    public static <T> Optional<T> first(Collection<T> collection) {
        if (Verifies.isEmpty(collection)) return Optional.empty();
        ArrayList<T> list = new ArrayList<>(collection);
        return Optional.ofNullable(list.get(0));
    }

    public static <T> Optional<T> last(Collection<T> collection) {
        if (Verifies.isEmpty(collection)) return Optional.empty();
        ArrayList<T> list = new ArrayList<>(collection);
        return Optional.ofNullable(list.get(list.size() - 1));
    }

    public static <T> Page<T> listPage(final List<T> list, final Integer page, final Integer size) {
        if (Verifies.isEmpty(list)) return Page.empty();
        List<T> autoPage = paging(list, page, size);
        return new Page<>(autoPage, list.size());
    }

    public static <T> List<T> paging(final List<T> list, Integer page, final Integer size) {
        Objects.requireNonNull(list);
        if (list.isEmpty()) return list;
        List<T> re = list;

        if (page != null && size != null) {
            page -= 1;
            if (page < 0 || size < 0) return new ArrayList<>();
            int start = page * size;
            int end = start + size;
            if (start > list.size()) {
                return new ArrayList<>();
            } else if (list.size() >= end) {
                re = re.subList(start, end);
            } else {
                re = re.subList(start, re.size());
            }
        }
        return re;
    }


    public static void forIndex(int start, int end, IntConsumer consumer) {
        Objects.requireNonNull(consumer);
        for (int i = start; i <= end; i++) {
            consumer.accept(i);
        }
    }

    public static void forIndex(long start, long end, LongConsumer consumer) {
        Objects.requireNonNull(consumer);
        for (long i = start; i <= end; i++) {
            consumer.accept(i);
        }
    }

    public static <T> void enumerate(Collection<T> collection, BiConsumer<T, Integer> consumer) {
        int i = 0;
        for (T item : collection) {
            consumer.accept(item, i++);
        }
    }

    public static void forIndexes(int[] start, int[] end, Consumer<int[]> consumer) {
        Objects.requireNonNull(start);
        Objects.requireNonNull(end);
        Objects.requireNonNull(consumer);
        int length = start.length;
        if (end.length != length) {
            throw new RuntimeException("索引数组长度不一致");
        }
        Function<int[], Boolean> each = indexes -> {
            int accIndex = length - 1;
            while (true) {
                int value = indexes[accIndex];
                int min = start[accIndex];
                int max = end[accIndex];
                if (value < max) {
                    indexes[accIndex] += 1;
                    return true;
                }
                if (accIndex == 0 && value == max) {
                    return false;
                }
                if (value == max) {
                    indexes[accIndex] = min;
                    accIndex--;
                }
            }
        };
        int[] indexes = new int[length];
        System.arraycopy(start, 0, indexes, 0, length);
        while (each.apply(indexes)) {
            consumer.accept(indexes);
        }

    }

    public static <T> List<List<T>> splitByLength(Collection<T> collection, int segmentLength) {
        if (Verifies.isEmpty(collection)) return new ArrayList<>();
        List<T> list = new ArrayList<>(collection);
        if (collection.size() <= segmentLength) {
            return Collections.singletonList(list);
        }
        int size = collection.size();
        int segSize = size / segmentLength;
        if (size % segmentLength != 0) segSize++;
        List<List<T>> array = new ArrayList<>(segSize);
        int start = 0;
        for (int i = 0; i < segSize - 1; i++) {
            array.add(list.subList(start, start + segmentLength));
            start += segmentLength;
        }
        array.add(list.subList(start, list.size()));
        return array;
    }

    /**
     * 按顺序和值分割,不是按值分组
     * 如果元素和前面的元素判断为相等则和前面的元素放入一组
     *
     * @param collection 集合
     * @param function   映射器
     * @param <T>        元素
     * @param <R>        映射的类型
     * @return List<List < T>>
     */
    public static <T, R> List<List<T>> splitByValue(Collection<T> collection, Function<T, R> function) {
        if (Verifies.isEmpty(collection)) return new ArrayList<>();
        Objects.requireNonNull(function);

        List<List<T>> groups = new ArrayList<>();
        if (collection.isEmpty()) return groups;
        if (collection.size() == 1) {
            groups.add(new ArrayList<>(collection));
            return groups;
        }
        List<T> list = new ArrayList<>(collection);
        T head = list.get(0);
        R val = function.apply(head);
        List<T> group = new ArrayList<>();
        group.add(head);
        for (int i = 1; i < list.size(); i++) {
            T elem = list.get(i);
            R valTemp = function.apply(elem);
            if (valTemp == val || valTemp.equals(val)) {
                group.add(elem);
                continue;
            }

            val = valTemp;
            groups.add(group);
            group = new ArrayList<>();
            group.add(elem);
        }
        if (!group.isEmpty()) groups.add(group);
        return groups;

    }


}
